﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Data.SQLite;
using PublicTransport.Services;

namespace PublicTransport.DAL
{
    /// <summary>   Trieda, ktorá pri inštancializácii nakopíruje obsah tabuliek reacheable_table do 
    /// 			pamäte, a volanie metód z tejto triedy v skutočnosti získa údaje priamo z pamäte,
    /// 			nie z podloženej databázy. 
    /// </summary>
    class ReacheableDao 
    {

        /// <summary> Pripojenie, z ktorého sa načítajú údaje do pamäte </summary>
        private SQLiteConnection connection;
        /// <summary> Tabuľka reacheable_table zoradená podľa primárneho kľúča, obsahuje údaje
        /// 		  ktoré spoje z tejto stanice odchádzajú</summary>
        private Dictionary<int, Dictionary<int, List<Zaznam>>> table;
        /// <summary> Tabuľka reacheable_table zoradená podľa primárneho kľúča, obsahuje údaje
        /// 		  ktoré spoje do tejto stanice vchádzajú</summary>
        private Dictionary<int, Dictionary<int, List<Zaznam>>> tableDestination;

        /// <summary>   Vytvorí spojenie s DB a načíta tabuľku do pamäte (časovo náročné,~ 30s) </summary>
        public ReacheableDao()
        {
            this.connection = DBHelper.GetConnection();
            table = new Dictionary<int, Dictionary<int, List<Zaznam>>>();
            tableDestination = new Dictionary<int, Dictionary<int, List<Zaznam>>>();
            FillDictionary();
        }

        /// <summary>   Metóda, ktorá vykoná samotné načítanie tabuľky do pamäte </summary>
        public void FillDictionary()
        {
            SQLiteCommand command = new SQLiteCommand(connection);
            command.CommandText = "SELECT * FROM reacheable_table";

            using (command)
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Zaznam zaznam = new Zaznam();
                        zaznam.Destination = int.Parse(reader["destination_station_id"].ToString());
                        zaznam.Distance = int.Parse(reader["distance"].ToString());
                        zaznam.Line = int.Parse(reader["line_id"].ToString());
                        zaznam.Start = int.Parse(reader["start_station_id"].ToString());

                        if (!table.ContainsKey(zaznam.Start))
                        {
                            table.Add(zaznam.Start, new Dictionary<int, List<Zaznam>>());
                        }
                        if (!table[zaznam.Start].ContainsKey(zaznam.Destination))
                        {
                            table[zaznam.Start].Add(zaznam.Destination, new List<Zaznam>());
                        }
                        table[zaznam.Start][zaznam.Destination].Add(zaznam);

                        // opposite direction 
                        if (!tableDestination.ContainsKey(zaznam.Destination))
                        {
                            tableDestination.Add(zaznam.Destination, new Dictionary<int, List<Zaznam>>());
                        }
                        if (!tableDestination[zaznam.Destination].ContainsKey(zaznam.Start))
                        {
                            tableDestination[zaznam.Destination].Add(zaznam.Start, new List<Zaznam>());
                        }
                        tableDestination[zaznam.Destination][zaznam.Start].Add(zaznam);
                    }

                }
            }
        }

        /// <summary>   Vráti dosiahnuteľné stanice z uvedenej zastávky </summary>
        ///
        /// <param name="id">   ID kľúč zastávky, z ktorej chceme získať dosiahnuteľné stanice</param>
        ///
        /// <returns>
        ///           Vráti enumerátor, ktorý dokáže prechádzať cez všetky dosiahnuteľné stanice z danej stanice.
        /// </returns>
        public IEnumerable<Zaznam> GetReachableStations(int id)
        {
            List<Zaznam> result = new List<Zaznam>();
            foreach (List<Zaznam> item in table[id].Values)
            {
                foreach (Zaznam item2 in item)
                {
                    result.Add(item2);
                }
            }

            return result;
        }


        /// <summary>   Vráti stanice, z ktorých sa dostaneme do uvedenej stanice </summary>
        ///
        /// <param name="id">   ID kľúč zastávky, z ktorej chceme získať stanice, ktoré do nej vchádzajú</param>
        ///
        /// <returns>
        ///           Vráti enumerátor, ktorý dokáže prechádzať cez všetky stanice, z ktorých sa dostaneme do určenej stanice.
        /// </returns>
        public IEnumerable<Zaznam> GetSourceStations(int id)
        {
            List<Zaznam> result = new List<Zaznam>();
            foreach (List<Zaznam> item in tableDestination[id].Values)
            {
                foreach (Zaznam item2 in item)
                {
                    result.Add(item2);
                }
            }

            return result;
        }

        /// <summary>   Vráti dosiahnuteľné stanice z uvedenej zastávky. </summary>
        ///
        /// <param name="id">           ID kľúč zastávky, z ktorej chceme získať dosiahnuteľné stanice. </param>
        /// <param name="excludedLine"> Spoje, ktoré chceme vynechať </param>
        ///
        /// <returns>
        ///     Vráti enumerátor, ktorý dokáže prechádzať cez všetky dosiahnuteľné stanice z danej
        ///     stanice.
        /// </returns>

        public IEnumerable<List<Zaznam>> GetReachableStations(int id, List<Line> excludedLine)
        {
            List<List<Zaznam>> result = new List<List<Zaznam>>();
            foreach (List<Zaznam> item in table[id].Values)
            {
                List<Zaznam> z = new List<Zaznam>();
                foreach (Zaznam item2 in item)
                {
                    if (excludedLine.Find(x => x.LineID == item2.Line) == null)
                    {
                        z.Add(item2);
                    }
                }
                if (z.Count != 0)
                {
                    result.Add(z);
                }
            }

            return result;
        }

        /// <summary>   Vráti stanice, z ktorých sa dostaneme do uvedenej stanice. </summary>
        ///
        /// <param name="id">           ID kľúč zastávky, z ktorej chceme získať stanice, ktoré do nej
        ///                             vchádzajú. </param>
        /// <param name="excludedLine"> Spoje, ktoré chceme vynechať. </param>
        ///
        /// <returns>
        ///     Vráti enumerátor, ktorý dokáže prechádzať cez všetky stanice, z ktorých sa dostaneme do
        ///     určenej stanice.
        /// </returns>

        public IEnumerable<List<Zaznam>> GetSourceStations(int id, List<Line> excludedLine)
        {
            List<List<Zaznam>> result = new List<List<Zaznam>>();
            foreach (List<Zaznam> item in tableDestination[id].Values)
            {
                List<Zaznam> z = new List<Zaznam>();
                foreach (Zaznam item2 in item)
                {
                    if (excludedLine.Find(x => x.LineID == item2.Line) == null)
                    {
                        z.Add(item2);
                    }
                }
                if (z.Count != 0)
                {
                    result.Add(z);
                }
            }

            return result;
        }
    }

    /// <summary>   Trieda na prenos údajov o stanici, ktorá je dosiahnuteľná z inej stanice. </summary>
    class Zaznam
    {
        public int Start { get; set; }
        public int Destination { get; set; }
        public int Line { get; set; }
        public int Distance { get; set; }
    }


}
